#if !ONLY_MISC_BUILD

using System;
using System.Collections.Generic;
using System.Reflection;
using System.Reflection.Emit;
using System.Threading;

namespace Demo.Mmose.LuaInterface
{
    /*
     * Structure to store a type and the return types of
     * its methods (the type of the returned value and out/ref
     * parameters).
     */
    struct LuaClassType
    {
        public Type klass;
        public Type[][] returnTypes;
    }

    /*
     * Common interface for types generated from tables. The method
     * returns the table that overrides some or all of the type's methods.
     */
    public interface ILuaGeneratedType
    {
        LuaTable __luaInterface_getLuaTable();
    }

    /*
     * Class used for generating delegates that get a function from the Lua
     * stack as a delegate of a specific type.
     * 
     * Author: Fabio Mascarenhas
     * Version: 1.0
     */
    class DelegateGenerator
    {
        private ObjectTranslator translator;
        private Type delegateType;

        public DelegateGenerator( ObjectTranslator translator, Type delegateType )
        {
            this.translator = translator;
            this.delegateType = delegateType;
        }
        public object extractGenerated( IntPtr luaState, int stackPos )
        {
            return CodeGeneration.Instance.GetDelegate( delegateType, translator.getFunction( luaState, stackPos ) );
        }
    }

    /*
     * Class used for generating delegates that get a table from the Lua
     * stack as a an object of a specific type.
     * 
     * Author: Fabio Mascarenhas
     * Version: 1.0
     */
    class ClassGenerator
    {
        private ObjectTranslator translator;
        private Type klass;

        public ClassGenerator( ObjectTranslator translator, Type klass )
        {
            this.translator = translator;
            this.klass = klass;
        }
        public object extractGenerated( IntPtr luaState, int stackPos )
        {
            return CodeGeneration.Instance.GetClassInstance( klass, translator.getTable( luaState, stackPos ) );
        }
    }

    /*
     * Dynamically generates new types from existing types and
     * Lua function and table values. Generated types are event handlers,
     * delegates, interface implementations and subclasses.
     * 
     * Author: Fabio Mascarenhas
     * Version: 1.0
     */
    class CodeGeneration
    {
        private Type eventHandlerParent = typeof( LuaEventHandler );
        private Dictionary<Type, Type> eventHandlerCollection = new Dictionary<Type, Type>();

        private Type delegateParent = typeof( LuaDelegate );
        private Dictionary<Type, Type> delegateCollection = new Dictionary<Type, Type>();

        private Type classHelper = typeof( LuaClassHelper );
        private Dictionary<Type, LuaClassType> classCollection = new Dictionary<Type, LuaClassType>();

        private AssemblyName assemblyName;
        private AssemblyBuilder newAssembly;
        private ModuleBuilder newModule;
        private int luaClassNumber = 1;
        private static readonly CodeGeneration instance = new CodeGeneration();

        static CodeGeneration()
        {
        }

        private CodeGeneration()
        {
            // Create an assembly name
            assemblyName = new AssemblyName();
            assemblyName.Name = "LuaInterface_generatedcode";
            // Create a new assembly with one module.
            newAssembly = Thread.GetDomain().DefineDynamicAssembly(
                assemblyName, AssemblyBuilderAccess.Run );
            newModule = newAssembly.DefineDynamicModule( "LuaInterface_generatedcode" );
        }

        /*
         * Singleton instance of the class
         */
        public static CodeGeneration Instance
        {
            get
            {
                return instance;
            }
        }

        /*
         *  Generates an event handler that calls a Lua function
         */
        private Type GenerateEvent( Type eventHandlerType )
        {
            string typeName;
            lock ( this )
            {
                typeName = "LuaGeneratedClass" + luaClassNumber;
                luaClassNumber++;
            }
            // Define a public class in the assembly, called typeName
            TypeBuilder myType = newModule.DefineType( typeName, TypeAttributes.Public, eventHandlerParent );

            // Defines the handler method. Its signature is void(object,<subclassofEventArgs>)
            Type[] paramTypes = new Type[2];
            paramTypes[0] = typeof( object );
            paramTypes[1] = eventHandlerType;
            Type returnType = typeof( void );
            MethodBuilder handleMethod = myType.DefineMethod( "HandleEvent",
                MethodAttributes.Public | MethodAttributes.HideBySig,
                returnType, paramTypes );

            // Emits the IL for the method. It loads the arguments
            // and calls the handleEvent method of the base class
            ILGenerator generator = handleMethod.GetILGenerator();
            generator.Emit( OpCodes.Ldarg_0 );
            generator.Emit( OpCodes.Ldarg_1 );
            generator.Emit( OpCodes.Ldarg_2 );
            MethodInfo miGenericEventHandler;
            miGenericEventHandler = eventHandlerParent.GetMethod( "handleEvent" );
            generator.Emit( OpCodes.Call, miGenericEventHandler );
            // returns
            generator.Emit( OpCodes.Ret );

            // creates the new type
            return myType.CreateType();
        }

        /*
         * Generates a type that can be used for instantiating a delegate
         * of the provided type, given a Lua function.
         */
        private Type GenerateDelegate( Type delegateType )
        {
            string typeName;
            lock ( this )
            {
                typeName = "LuaGeneratedClass" + luaClassNumber;
                luaClassNumber++;
            }
            // Define a public class in the assembly, called typeName
            TypeBuilder myType = newModule.DefineType( typeName, TypeAttributes.Public, delegateParent );

            // Defines the delegate method with the same signature as the
            // Invoke method of delegateType
            MethodInfo invokeMethod = delegateType.GetMethod( "Invoke" );
            ParameterInfo[] paramInfo = invokeMethod.GetParameters();
            Type[] paramTypes = new Type[paramInfo.Length];
            Type returnType = invokeMethod.ReturnType;

            // Counts out and ref params, for use later
            int nOutParams = 0; int nOutAndRefParams = 0;
            for ( int i = 0; i < paramTypes.Length; i++ )
            {
                paramTypes[i] = paramInfo[i].ParameterType;
                if ( ( !paramInfo[i].IsIn ) && paramInfo[i].IsOut )
                    nOutParams++;
                if ( paramTypes[i].IsByRef )
                    nOutAndRefParams++;
            }
            int[] refArgs = new int[nOutAndRefParams];

            MethodBuilder delegateMethod = myType.DefineMethod( "CallFunction",
                invokeMethod.Attributes,
                returnType, paramTypes );

            // Generates the IL for the method
            ILGenerator generator = delegateMethod.GetILGenerator();

            generator.DeclareLocal( typeof( object[] ) ); // original arguments
            generator.DeclareLocal( typeof( object[] ) ); // with out-only arguments removed
            generator.DeclareLocal( typeof( int[] ) ); // indexes of out and ref arguments
            if ( !( returnType == typeof( void ) ) )  // return value
                generator.DeclareLocal( returnType );
            else
                generator.DeclareLocal( typeof( object ) );
            // Initializes local variables
            generator.Emit( OpCodes.Ldc_I4, paramTypes.Length );
            generator.Emit( OpCodes.Newarr, typeof( object ) );
            generator.Emit( OpCodes.Stloc_0 );
            generator.Emit( OpCodes.Ldc_I4, paramTypes.Length - nOutParams );
            generator.Emit( OpCodes.Newarr, typeof( object ) );
            generator.Emit( OpCodes.Stloc_1 );
            generator.Emit( OpCodes.Ldc_I4, nOutAndRefParams );
            generator.Emit( OpCodes.Newarr, typeof( int ) );
            generator.Emit( OpCodes.Stloc_2 );
            // Stores the arguments in the local variables
            for ( int iArgs = 0, iInArgs = 0, iOutArgs = 0; iArgs < paramTypes.Length; iArgs++ )
            {
                generator.Emit( OpCodes.Ldloc_0 );
                generator.Emit( OpCodes.Ldc_I4, iArgs );
                generator.Emit( OpCodes.Ldarg, iArgs + 1 );
                if ( paramTypes[iArgs].IsByRef )
                {
                    if ( paramTypes[iArgs].GetElementType().IsValueType )
                    {
                        generator.Emit( OpCodes.Ldobj, paramTypes[iArgs].GetElementType() );
                        generator.Emit( OpCodes.Box, paramTypes[iArgs].GetElementType() );
                    }
                    else generator.Emit( OpCodes.Ldind_Ref );
                }
                else
                {
                    if ( paramTypes[iArgs].IsValueType )
                        generator.Emit( OpCodes.Box, paramTypes[iArgs] );
                }
                generator.Emit( OpCodes.Stelem_Ref );
                if ( paramTypes[iArgs].IsByRef )
                {
                    generator.Emit( OpCodes.Ldloc_2 );
                    generator.Emit( OpCodes.Ldc_I4, iOutArgs );
                    generator.Emit( OpCodes.Ldc_I4, iArgs );
                    generator.Emit( OpCodes.Stelem_I4 );
                    refArgs[iOutArgs] = iArgs;
                    iOutArgs++;
                }
                if ( paramInfo[iArgs].IsIn || ( !paramInfo[iArgs].IsOut ) )
                {
                    generator.Emit( OpCodes.Ldloc_1 );
                    generator.Emit( OpCodes.Ldc_I4, iInArgs );
                    generator.Emit( OpCodes.Ldarg, iArgs + 1 );
                    if ( paramTypes[iArgs].IsByRef )
                    {
                        if ( paramTypes[iArgs].GetElementType().IsValueType )
                        {
                            generator.Emit( OpCodes.Ldobj, paramTypes[iArgs].GetElementType() );
                            generator.Emit( OpCodes.Box, paramTypes[iArgs].GetElementType() );
                        }
                        else generator.Emit( OpCodes.Ldind_Ref );
                    }
                    else
                    {
                        if ( paramTypes[iArgs].IsValueType )
                            generator.Emit( OpCodes.Box, paramTypes[iArgs] );
                    }
                    generator.Emit( OpCodes.Stelem_Ref );
                    iInArgs++;
                }
            }
            // Calls the callFunction method of the base class
            generator.Emit( OpCodes.Ldarg_0 );
            generator.Emit( OpCodes.Ldloc_0 );
            generator.Emit( OpCodes.Ldloc_1 );
            generator.Emit( OpCodes.Ldloc_2 );
            MethodInfo miGenericEventHandler;
            miGenericEventHandler = delegateParent.GetMethod( "callFunction" );
            generator.Emit( OpCodes.Call, miGenericEventHandler );
            // Stores return value
            if ( returnType == typeof( void ) )
            {
                generator.Emit( OpCodes.Pop );
                generator.Emit( OpCodes.Ldnull );
            }
            else if ( returnType.IsValueType )
            {
                generator.Emit( OpCodes.Unbox, returnType );
                generator.Emit( OpCodes.Ldobj, returnType );
            }
            else generator.Emit( OpCodes.Castclass, returnType );
            generator.Emit( OpCodes.Stloc_3 );
            // Stores new value of out and ref params
            for ( int i = 0; i < refArgs.Length; i++ )
            {
                generator.Emit( OpCodes.Ldarg, refArgs[i] + 1 );
                generator.Emit( OpCodes.Ldloc_0 );
                generator.Emit( OpCodes.Ldc_I4, refArgs[i] );
                generator.Emit( OpCodes.Ldelem_Ref );
                if ( paramTypes[refArgs[i]].GetElementType().IsValueType )
                {
                    generator.Emit( OpCodes.Unbox, paramTypes[refArgs[i]].GetElementType() );
                    generator.Emit( OpCodes.Ldobj, paramTypes[refArgs[i]].GetElementType() );
                    generator.Emit( OpCodes.Stobj, paramTypes[refArgs[i]].GetElementType() );
                }
                else
                {
                    generator.Emit( OpCodes.Castclass, paramTypes[refArgs[i]].GetElementType() );
                    generator.Emit( OpCodes.Stind_Ref );
                }
            }
            // Returns
            if ( !( returnType == typeof( void ) ) )
                generator.Emit( OpCodes.Ldloc_3 );
            generator.Emit( OpCodes.Ret );

            // creates the new type
            return myType.CreateType();
        }

        /*
         * Generates an implementation of klass, if it is an interface, or
         * a subclass of klass that delegates its virtual methods to a Lua table.
         */
        public void GenerateClass( Type klass, out Type newType, out Type[][] returnTypes )
        {
            string typeName;
            lock ( this )
            {
                typeName = "LuaGeneratedClass" + luaClassNumber;
                luaClassNumber++;
            }
            TypeBuilder myType;
            // Define a public class in the assembly, called typeName
            if ( klass.IsInterface )
                myType = newModule.DefineType( typeName, TypeAttributes.Public, typeof( object ), new Type[] { klass, typeof( ILuaGeneratedType ) } );
            else
                myType = newModule.DefineType( typeName, TypeAttributes.Public, klass, new Type[] { typeof( ILuaGeneratedType ) } );
            // Field that stores the Lua table
            FieldBuilder luaTableField = myType.DefineField( "__luaInterface_luaTable", typeof( LuaTable ), FieldAttributes.Public );
            // Field that stores the return types array
            FieldBuilder returnTypesField = myType.DefineField( "__luaInterface_returnTypes", typeof( Type[][] ), FieldAttributes.Public );
            // Generates the constructor for the new type, it takes a Lua table and an array
            // of return types and stores them in the respective fields
            ConstructorBuilder constructor =
                myType.DefineConstructor( MethodAttributes.Public, CallingConventions.Standard, new Type[] { typeof( LuaTable ), typeof( Type[][] ) } );
            ILGenerator generator = constructor.GetILGenerator();
            generator.Emit( OpCodes.Ldarg_0 );
            if ( klass.IsInterface )
                generator.Emit( OpCodes.Call, typeof( object ).GetConstructor( Type.EmptyTypes ) );
            else
                generator.Emit( OpCodes.Call, klass.GetConstructor( Type.EmptyTypes ) );
            generator.Emit( OpCodes.Ldarg_0 );
            generator.Emit( OpCodes.Ldarg_1 );
            generator.Emit( OpCodes.Stfld, luaTableField );
            generator.Emit( OpCodes.Ldarg_0 );
            generator.Emit( OpCodes.Ldarg_2 );
            generator.Emit( OpCodes.Stfld, returnTypesField );
            generator.Emit( OpCodes.Ret );
            // Generates overriden versions of the klass' public virtual methods
            MethodInfo[] classMethods = klass.GetMethods();
            returnTypes = new Type[classMethods.Length][];
            int i = 0;
            foreach ( MethodInfo method in classMethods )
            {
                if ( klass.IsInterface )
                {
                    GenerateMethod( myType, method,
                        MethodAttributes.HideBySig | MethodAttributes.Virtual | MethodAttributes.NewSlot,
                        i, luaTableField, returnTypesField, false, out returnTypes[i] );
                    i++;
                }
                else
                {
                    if ( !method.IsPrivate && !method.IsFinal && method.IsVirtual )
                    {
                        GenerateMethod( myType, method, ( method.Attributes | MethodAttributes.NewSlot ) ^ MethodAttributes.NewSlot, i,
                            luaTableField, returnTypesField, true, out returnTypes[i] );
                        i++;
                    }
                }
            }
            // Generates an implementation of the __luaInterface_getLuaTable method
            MethodBuilder returnTableMethod = myType.DefineMethod( "__luaInterface_getLuaTable",
                MethodAttributes.Public | MethodAttributes.HideBySig | MethodAttributes.Virtual,
                typeof( LuaTable ), new Type[0] );
            myType.DefineMethodOverride( returnTableMethod, typeof( ILuaGeneratedType ).GetMethod( "__luaInterface_getLuaTable" ) );
            generator = returnTableMethod.GetILGenerator();
            generator.Emit( OpCodes.Ldfld, luaTableField );
            generator.Emit( OpCodes.Ret );
            // Creates the type
            newType = myType.CreateType();
        }

        /*
         * Generates an overriden implementation of method inside myType that delegates
         * to a function in a Lua table with the same name, if the function exists. If it
         * doesn't the method calls the base method (or does nothing, in case of interface
         * implementations).
         */
        private void GenerateMethod( TypeBuilder myType, MethodInfo method, MethodAttributes attributes,
            int methodIndex, FieldInfo luaTableField, FieldInfo returnTypesField, bool generateBase, out Type[] returnTypes )
        {
            ParameterInfo[] paramInfo = method.GetParameters();
            Type[] paramTypes = new Type[paramInfo.Length];
            List<Type> returnTypesList = new List<Type>();

            // Counts out and ref parameters, for later use,
            // and creates the list of return types
            int nOutParams = 0; int nOutAndRefParams = 0;
            Type returnType = method.ReturnType;
            returnTypesList.Add( returnType );
            for ( int i = 0; i < paramTypes.Length; i++ )
            {
                paramTypes[i] = paramInfo[i].ParameterType;
                if ( ( !paramInfo[i].IsIn ) && paramInfo[i].IsOut )
                    nOutParams++;
                if ( paramTypes[i].IsByRef )
                {
                    returnTypesList.Add( paramTypes[i].GetElementType() );
                    nOutAndRefParams++;
                }
            }
            int[] refArgs = new int[nOutAndRefParams];
            returnTypes = returnTypesList.ToArray();

            // Generates a version of the method that calls the base implementation
            // directly, for use by the base field of the table
            if ( generateBase )
            {
                MethodBuilder baseMethod = myType.DefineMethod( "__luaInterface_base_" + method.Name,
                    MethodAttributes.Private | MethodAttributes.NewSlot | MethodAttributes.HideBySig,
                    returnType, paramTypes );
                ILGenerator generatorBase = baseMethod.GetILGenerator();
                generatorBase.Emit( OpCodes.Ldarg_0 );
                for ( int i = 0; i < paramTypes.Length; i++ )
                    generatorBase.Emit( OpCodes.Ldarg, i + 1 );
                generatorBase.Emit( OpCodes.Call, method );
                if ( returnType == typeof( void ) )
                    generatorBase.Emit( OpCodes.Pop );
                generatorBase.Emit( OpCodes.Ret );
            }

            // Defines the method
            MethodBuilder methodImpl = myType.DefineMethod( method.Name, attributes,
                returnType, paramTypes );
            // If it's an implementation of an interface tells what method it
            // is overriding
            if ( myType.BaseType.Equals( typeof( object ) ) )
                myType.DefineMethodOverride( methodImpl, method );

            ILGenerator generator = methodImpl.GetILGenerator();

            generator.DeclareLocal( typeof( object[] ) ); // original arguments
            generator.DeclareLocal( typeof( object[] ) ); // with out-only arguments removed
            generator.DeclareLocal( typeof( int[] ) ); // indexes of out and ref arguments
            if ( !( returnType == typeof( void ) ) ) // return value
                generator.DeclareLocal( returnType );
            else
                generator.DeclareLocal( typeof( object ) );
            // Initializes local variables
            generator.Emit( OpCodes.Ldc_I4, paramTypes.Length );
            generator.Emit( OpCodes.Newarr, typeof( object ) );
            generator.Emit( OpCodes.Stloc_0 );
            generator.Emit( OpCodes.Ldc_I4, paramTypes.Length - nOutParams + 1 );
            generator.Emit( OpCodes.Newarr, typeof( object ) );
            generator.Emit( OpCodes.Stloc_1 );
            generator.Emit( OpCodes.Ldc_I4, nOutAndRefParams );
            generator.Emit( OpCodes.Newarr, typeof( int ) );
            generator.Emit( OpCodes.Stloc_2 );
            generator.Emit( OpCodes.Ldloc_1 );
            generator.Emit( OpCodes.Ldc_I4_0 );
            generator.Emit( OpCodes.Ldarg_0 );
            generator.Emit( OpCodes.Ldfld, luaTableField );
            generator.Emit( OpCodes.Stelem_Ref );
            // Stores the arguments into the local variables, as needed
            for ( int iArgs = 0, iInArgs = 1, iOutArgs = 0; iArgs < paramTypes.Length; iArgs++ )
            {
                generator.Emit( OpCodes.Ldloc_0 );
                generator.Emit( OpCodes.Ldc_I4, iArgs );
                generator.Emit( OpCodes.Ldarg, iArgs + 1 );
                if ( paramTypes[iArgs].IsByRef )
                {
                    if ( paramTypes[iArgs].GetElementType().IsValueType )
                    {
                        generator.Emit( OpCodes.Ldobj, paramTypes[iArgs].GetElementType() );
                        generator.Emit( OpCodes.Box, paramTypes[iArgs].GetElementType() );
                    }
                    else generator.Emit( OpCodes.Ldind_Ref );
                }
                else
                {
                    if ( paramTypes[iArgs].IsValueType )
                        generator.Emit( OpCodes.Box, paramTypes[iArgs] );
                }
                generator.Emit( OpCodes.Stelem_Ref );
                if ( paramTypes[iArgs].IsByRef )
                {
                    generator.Emit( OpCodes.Ldloc_2 );
                    generator.Emit( OpCodes.Ldc_I4, iOutArgs );
                    generator.Emit( OpCodes.Ldc_I4, iArgs );
                    generator.Emit( OpCodes.Stelem_I4 );
                    refArgs[iOutArgs] = iArgs;
                    iOutArgs++;
                }
                if ( paramInfo[iArgs].IsIn || ( !paramInfo[iArgs].IsOut ) )
                {
                    generator.Emit( OpCodes.Ldloc_1 );
                    generator.Emit( OpCodes.Ldc_I4, iInArgs );
                    generator.Emit( OpCodes.Ldarg, iArgs + 1 );
                    if ( paramTypes[iArgs].IsByRef )
                    {
                        if ( paramTypes[iArgs].GetElementType().IsValueType )
                        {
                            generator.Emit( OpCodes.Ldobj, paramTypes[iArgs].GetElementType() );
                            generator.Emit( OpCodes.Box, paramTypes[iArgs].GetElementType() );
                        }
                        else generator.Emit( OpCodes.Ldind_Ref );
                    }
                    else
                    {
                        if ( paramTypes[iArgs].IsValueType )
                            generator.Emit( OpCodes.Box, paramTypes[iArgs] );
                    }
                    generator.Emit( OpCodes.Stelem_Ref );
                    iInArgs++;
                }
            }
            // Gets the function the method will delegate to by calling
            // the getTableFunction method of class LuaClassHelper
            generator.Emit( OpCodes.Ldarg_0 );
            generator.Emit( OpCodes.Ldfld, luaTableField );
            generator.Emit( OpCodes.Ldstr, method.Name );
            generator.Emit( OpCodes.Call, classHelper.GetMethod( "getTableFunction" ) );
            Label lab1 = generator.DefineLabel();
            generator.Emit( OpCodes.Dup );
            generator.Emit( OpCodes.Brtrue_S, lab1 );
            // Function does not exist, call base method
            generator.Emit( OpCodes.Pop );
            if ( !method.IsAbstract )
            {
                generator.Emit( OpCodes.Ldarg_0 );
                for ( int i = 0; i < paramTypes.Length; i++ )
                    generator.Emit( OpCodes.Ldarg, i + 1 );
                generator.Emit( OpCodes.Call, method );
                if ( returnType == typeof( void ) )
                    generator.Emit( OpCodes.Pop );
                generator.Emit( OpCodes.Ret );
                generator.Emit( OpCodes.Ldnull );
            }
            else
                generator.Emit( OpCodes.Ldnull );
            Label lab2 = generator.DefineLabel();
            generator.Emit( OpCodes.Br_S, lab2 );
            generator.MarkLabel( lab1 );
            // Function exists, call using method callFunction of LuaClassHelper
            generator.Emit( OpCodes.Ldloc_0 );
            generator.Emit( OpCodes.Ldarg_0 );
            generator.Emit( OpCodes.Ldfld, returnTypesField );
            generator.Emit( OpCodes.Ldc_I4, methodIndex );
            generator.Emit( OpCodes.Ldelem_Ref );
            generator.Emit( OpCodes.Ldloc_1 );
            generator.Emit( OpCodes.Ldloc_2 );
            generator.Emit( OpCodes.Call, classHelper.GetMethod( "callFunction" ) );
            generator.MarkLabel( lab2 );
            // Stores the function return value
            if ( returnType == typeof( void ) )
            {
                generator.Emit( OpCodes.Pop );
                generator.Emit( OpCodes.Ldnull );
            }
            else if ( returnType.IsValueType )
            {
                generator.Emit( OpCodes.Unbox, returnType );
                generator.Emit( OpCodes.Ldobj, returnType );
            }
            else generator.Emit( OpCodes.Castclass, returnType );
            generator.Emit( OpCodes.Stloc_3 );
            // Sets return values of out and ref parameters
            for ( int i = 0; i < refArgs.Length; i++ )
            {
                generator.Emit( OpCodes.Ldarg, refArgs[i] + 1 );
                generator.Emit( OpCodes.Ldloc_0 );
                generator.Emit( OpCodes.Ldc_I4, refArgs[i] );
                generator.Emit( OpCodes.Ldelem_Ref );
                if ( paramTypes[refArgs[i]].GetElementType().IsValueType )
                {
                    generator.Emit( OpCodes.Unbox, paramTypes[refArgs[i]].GetElementType() );
                    generator.Emit( OpCodes.Ldobj, paramTypes[refArgs[i]].GetElementType() );
                    generator.Emit( OpCodes.Stobj, paramTypes[refArgs[i]].GetElementType() );
                }
                else
                {
                    generator.Emit( OpCodes.Castclass, paramTypes[refArgs[i]].GetElementType() );
                    generator.Emit( OpCodes.Stind_Ref );
                }
            }

            // Returns
            if ( !( returnType == typeof( void ) ) )
                generator.Emit( OpCodes.Ldloc_3 );
            generator.Emit( OpCodes.Ret );
        }

        /*
         * Gets an event handler for the event type that delegates to the eventHandler Lua function.
         * Caches the generated type.
         */
        public LuaEventHandler GetEvent( Type eventHandlerType, LuaFunction eventHandler )
        {
            Type eventConsumerType;
            if ( eventHandlerCollection.ContainsKey( eventHandlerType ) )
            {
                eventConsumerType = eventHandlerCollection[eventHandlerType];
            }
            else
            {
                eventConsumerType = GenerateEvent( eventHandlerType );
                eventHandlerCollection[eventHandlerType] = eventConsumerType;
            }
            LuaEventHandler luaEventHandler = (LuaEventHandler)Activator.CreateInstance( eventConsumerType );
            luaEventHandler.handler = eventHandler;
            return luaEventHandler;
        }

        /*
         * Gets a delegate with delegateType that calls the luaFunc Lua function
         * Caches the generated type.
         */
        public Delegate GetDelegate( Type delegateType, LuaFunction luaFunc )
        {
            List<Type> returnTypes = new List<Type>();
            Type luaDelegateType;
            if ( delegateCollection.ContainsKey( delegateType ) )
            {
                luaDelegateType = delegateCollection[delegateType];
            }
            else
            {
                luaDelegateType = GenerateDelegate( delegateType );
                delegateCollection[delegateType] = luaDelegateType;
            }
            MethodInfo methodInfo = delegateType.GetMethod( "Invoke" );
            returnTypes.Add( methodInfo.ReturnType );
            foreach ( ParameterInfo paramInfo in methodInfo.GetParameters() )
                if ( paramInfo.ParameterType.IsByRef )
                    returnTypes.Add( paramInfo.ParameterType );
            LuaDelegate luaDelegate = (LuaDelegate)Activator.CreateInstance( luaDelegateType );
            luaDelegate.function = luaFunc;
            luaDelegate.returnTypes = returnTypes.ToArray();
            return Delegate.CreateDelegate( delegateType, luaDelegate, "CallFunction" );
        }

        /*
         * Gets an instance of an implementation of the klass interface or
         * subclass of klass that delegates public virtual methods to the
         * luaTable table.
         * Caches the generated type.
         */
        public object GetClassInstance( Type klass, LuaTable luaTable )
        {
            LuaClassType luaClassType;
            if ( classCollection.ContainsKey( klass ) )
            {
                luaClassType = classCollection[klass];
            }
            else
            {
                luaClassType = new LuaClassType();
                GenerateClass( klass, out luaClassType.klass, out luaClassType.returnTypes );
                classCollection[klass] = luaClassType;
            }
            return Activator.CreateInstance( luaClassType.klass, new object[] { luaTable, luaClassType.returnTypes } );
        }
    }
}

#endif